package com.yikome.purchase.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.itmuch.lightsecurity.jwt.User;
import com.yikome.common.CommonService;
import com.yikome.delivery.enums.DeliveryStatus;
import com.yikome.mq.purchase.request.PurchaseStorageProductRequest;
import com.yikome.order.enums.OrderStatus;
import com.yikome.purchase.dto.PurchaseLogisticsSearchDTO;
import com.yikome.purchase.dto.PurchaseProductSearchDTO;
import com.yikome.purchase.dto.PurchaseSearchDTO;
import com.yikome.purchase.dto.PurchaseSimpleExportDTO;
import com.yikome.purchase.entity.Purchase;
import com.yikome.purchase.entity.PurchaseLogistics;
import com.yikome.purchase.entity.PurchaseProduct;
import com.yikome.purchase.enums.PurchaseStatus;
import com.yikome.purchase.repository.PurchaseLogisticsRepository;
import com.yikome.purchase.repository.PurchaseProductRepository;
import com.yikome.purchase.repository.PurchaseRepository;
import com.yikome.purchase.request.PendingPurchaseProductRequest;
import com.yikome.purchase.request.PurchaseCreateRequest;
import com.yikome.purchase.request.PurchaseLogisticsRequest;
import com.yikome.purchase.request.PurchaseProductCreateRequest;
import com.yikome.purchase.request.PurchaseProductUpdateRequest;
import com.yikome.purchase.request.PurchaseSearchRequest;
import com.yikome.purchase.request.PurchaseUpdateRequest;
import com.yikome.purchase.response.PendingPurchaseProductResponse;
import com.yikome.purchase.schedule.AsyncPurchaseSchedule;
import com.yikome.storehouse.entity.Inventory;
import com.yikome.storehouse.repository.InventoryRepository;
import com.yikome.utils.BeanUtils;
import com.yikome.utils.NamedParameterJdbcUtils;
import com.yikome.utils.SecurityUtils;

import cn.hutool.core.util.StrUtil;

@Service
public class PurchaseService extends CommonService<Purchase, Long> {

	@Autowired
	private PurchaseRepository purchaseRepository ; 
	
	@Autowired
	private PurchaseProductRepository purchaseProductRepository ;
	
	@Autowired
	private PurchaseLogisticsRepository purchaseLogisticsRepository ;
	
	@Autowired
	private InventoryRepository inventoryRepository ;
	
	@Autowired
	private AsyncPurchaseSchedule purchaseSchedule ; 

	@Autowired
	private NamedParameterJdbcTemplate jdbcTemplate ;

	public Page<PendingPurchaseProductResponse> pendingPurchase(PendingPurchaseProductRequest pendingPurchaseProductRequest , Pageable pageable){
		
		String contentSQL = "SELECT \n" + 
				"	s_storehouse.storehouse_name, order_vo.*, (IFNULL(order_vo.order_product_quantities,0) + IFNULL(delivery_vo.delivery_quantities,0) - IFNULL(purchase_vo.purchased_quantities,0) - IFNULL(s_inventory.inventory,0))quantity \n" + 
				"FROM \n" + 
				"( \n" + 
				"	SELECT \n" + 
				"		o_order.storehouse_id, o_order_product.product_id, o_order_product.product_title, o_order_product.product_sku_id, o_order_product.product_sku_barcode, o_order_product.product_sku_name, o_order_product.product_image_url product_sku_image, \n" + 
				"		o_order_product.source_url, o_order_product.supplier_id, o_order_product.supplier_name, o_order_product.source_id, o_order_product.source_number, o_order_product.reference_purchase_price , SUM(o_order_product.quantity)order_product_quantities \n" + 
				"	FROM o_order \n" + 
				"		LEFT JOIN o_order_product ON (o_order.`code` = o_order_product.order_code AND o_order_product.yn = 1) \n" + 
				"	WHERE 1 = 1 AND o_order.yn = 1 AND o_order.`status` = :orderStatus \n" + 
				"	GROUP BY o_order_product.product_sku_barcode \n" + 
				") order_vo \n" + 
				"LEFT JOIN \n" + 
				"( \n" + 
				"	SELECT \n" + 
				"		p_purchase.storehouse_id, p_purchase_product.product_sku_barcode, SUM((p_purchase_product.purchased_quantity - p_purchase_product.storaged_quantity - p_purchase_product.cancel_quantity)) purchased_quantities \n" + 
				"	FROM p_purchase \n" + 
				"		LEFT JOIN p_purchase_product ON (p_purchase.purchase_code = p_purchase_product.purchase_code AND p_purchase_product.yn = 1 ) \n" + 
				"	WHERE p_purchase.yn = 1 AND (p_purchase.`status` in (:purchaseStatus))  \n" + 
				"	AND (p_purchase_product.purchased_quantity - p_purchase_product.storaged_quantity - p_purchase_product.cancel_quantity) > 0 \n" + 
				"	GROUP BY p_purchase.storehouse_id, p_purchase_product.product_sku_barcode \n" + 
				")purchase_vo ON (order_vo.product_sku_barcode = purchase_vo.product_sku_barcode AND order_vo.storehouse_id = purchase_vo.storehouse_id) \n" + 
				"LEFT JOIN \n" + 
				"( \n" + 
				"	SELECT \n" + 
				"		d_delivery.storehouse_id, d_delivery_details.product_sku_barcode , SUM(d_delivery_details.quantity) delivery_quantities \n" + 
				"	FROM d_delivery \n" + 
				"		LEFT JOIN d_delivery_details ON (d_delivery_details.delivery_code = d_delivery.delivery_code AND d_delivery_details.yn = 1) \n" + 
				"	WHERE d_delivery.yn = 1 AND (d_delivery.`status` in (:deliveryStatus))  \n" + 
				"	GROUP BY d_delivery.storehouse_id, d_delivery_details.product_sku_barcode \n" + 
				")delivery_vo ON (order_vo.product_sku_barcode = delivery_vo.product_sku_barcode AND order_vo.storehouse_id = delivery_vo.storehouse_id) \n" + 
				"LEFT JOIN s_inventory ON (order_vo.storehouse_id = s_inventory.storehouse_id AND order_vo.product_sku_barcode = s_inventory.product_sku_barcode AND s_inventory.yn = 1) \n" + 
				"LEFT JOIN s_storehouse ON (order_vo.storehouse_id = s_storehouse.id) \n" + 
				"WHERE 1 = 1 \n" + 
				"AND (IFNULL(order_vo.order_product_quantities,0) + IFNULL(delivery_vo.delivery_quantities,0) - IFNULL(purchase_vo.purchased_quantities,0) - IFNULL(s_inventory.inventory,0)) > 0 \n";
		
		
		Map<String, Object> paramsMap = new HashMap<>();
		paramsMap.put("orderStatus", OrderStatus.ORDER_STOCK.getStatus());
		paramsMap.put("purchaseStatus", ImmutableList.of(PurchaseStatus.PURCHASE_CREATE.getStatus(), PurchaseStatus.PURCHASE_ASYNC.getStatus()));
		paramsMap.put("deliveryStatus", ImmutableList.of(DeliveryStatus.DELIVERY_CREATED.getStatus(), DeliveryStatus.DELIVERY_ASYNC.getStatus()));
		
		StringBuilder paramSQL = new StringBuilder("");
		if(pendingPurchaseProductRequest.getStorehouseId() != null) {
			paramSQL.append("AND order_vo.storehouse_id = :storehouseId \n");
			paramsMap.put("storehouseId", pendingPurchaseProductRequest.getStorehouseId());
		}
		if(pendingPurchaseProductRequest.getProductId() != null) {
			paramSQL.append("AND order_vo.product_id = :productId \n");
			paramsMap.put("productId", pendingPurchaseProductRequest.getProductId());
		}
		if(pendingPurchaseProductRequest.getSupplierId() != null) {
			paramSQL.append("AND order_vo.supplier_id = :supplierId \n");
			paramsMap.put("supplierId", pendingPurchaseProductRequest.getSupplierId());
		}
		if(StrUtil.isNotBlank(pendingPurchaseProductRequest.getProductSkuBarcode())) {
			paramSQL.append("AND order_vo.product_sku_barcode = :productSkuBarcode \n");
			paramsMap.put("productSkuBarcode", pendingPurchaseProductRequest.getProductSkuBarcode());
		}
		
		String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
		String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
		
		List<PendingPurchaseProductResponse> content = jdbcTemplate.query(contentSQL  + paramSQL + orderSQL + pageSQL, paramsMap,  new BeanPropertyRowMapper<>(PendingPurchaseProductResponse.class));
		
		
		Long total = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM (" + contentSQL + paramSQL + ") T", paramsMap, Long.class);
		
		return new PageImpl<>(content, pageable, total);
		
	}
	
	@Transactional
	public Purchase createPurchase(PurchaseCreateRequest purchaseCreateRequest) {
		
		Preconditions.checkArgument(StrUtil.isNotBlank(purchaseCreateRequest.getPurchaseCode()), "采购单号不存在");
		Preconditions.checkArgument(purchaseCreateRequest.getStorehouseId() != null, "仓库未指定");
		Preconditions.checkArgument(purchaseCreateRequest.getPurchasePrice() != null, "采购价格不能为空");
		Purchase findPurchase = purchaseRepository.findPurchase(purchaseCreateRequest.getPurchaseCode());
		Preconditions.checkArgument(findPurchase == null, "采购单号已存在过,不能重复使用");
		
		if(purchaseCreateRequest.getPurchaseFreightPrice() == null) purchaseCreateRequest.setPurchaseFreightPrice(BigDecimal.ZERO);
		
		List<PurchaseProductCreateRequest> purchaseProductCreateRequests = purchaseCreateRequest.getPurchaseProductCreateRequests();
		Preconditions.checkArgument(purchaseProductCreateRequests != null && !purchaseProductCreateRequests.isEmpty(), "采购详情不存在");
		
		for (PurchaseProductCreateRequest purchaseProductCreateRequest : purchaseProductCreateRequests) {
			
			Preconditions.checkArgument(purchaseProductCreateRequest.getPurchasedPrice() != null, "采购单商品信息不完整");
			Preconditions.checkArgument(purchaseProductCreateRequest.getPurchasedQuantity() != null, "采购单商品信息不完整");
			Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductCreateRequest.getProductTitle()), "采购单商品信息不完整");
			Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductCreateRequest.getProductSkuBarcode()), "采购单商品信息不完整");
			Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductCreateRequest.getProductSkuName()), "采购单商品信息不完整");
		}
		
		int purchasedTotalQuantity = 0 ; 
		List<PurchaseProduct> purchaseProducts = new ArrayList<>() ; 
		for (PurchaseProductCreateRequest purchaseProductCreateRequest : purchaseProductCreateRequests) {
			
			PurchaseProduct purchaseProduct = new PurchaseProduct();
			BeanUtils.copyProperties(purchaseProductCreateRequest, purchaseProduct);
			
			purchaseProduct.setPurchaseCode(purchaseCreateRequest.getPurchaseCode());
			purchaseProduct.setStoragedQuantity(0);
			purchaseProduct.setCancelQuantity(0);
			purchasedTotalQuantity += purchaseProductCreateRequest.getPurchasedQuantity();
			
			if(StrUtil.isBlank(purchaseProduct.getProductSkuImage())) {
				
				String imgCount = "SELECT COUNT(1) FROM o_order_product WHERE o_order_product.product_sku_barcode = :productSkuBarcode";
				
				Long imgCounts = jdbcTemplate.queryForObject(imgCount, ImmutableMap.of("productSkuBarcode", purchaseProduct.getProductSkuBarcode()), Long.class);
				if(imgCounts > 0) {
					purchaseProduct.setProductSkuImage(jdbcTemplate.queryForObject("SELECT o_order_product.product_image_url FROM o_order_product WHERE o_order_product.product_sku_barcode = :productSkuBarcode LIMIT 1", ImmutableMap.of("productSkuBarcode", purchaseProduct.getProductSkuBarcode()), String.class));
				}
			}
			
			purchaseProducts.add(purchaseProduct);
		}
		
		Purchase purchase = new Purchase();
		BeanUtils.copyProperties(purchaseCreateRequest, purchase);
		purchase.setPurchaseCode(purchaseCreateRequest.getPurchaseCode().trim());
		purchase.setPurchasedTotalQuantity(purchasedTotalQuantity);
		purchase.setStoragedTotalQuantity(0);
		purchase.setCancelTotalQuantity(0);
		purchase.setStatus(PurchaseStatus.PURCHASE_CREATE.getStatus());
		
		purchaseRepository.save(purchase);
		purchaseProductRepository.saveAll(purchaseProducts);
		
		//远程操作
		purchaseSchedule.asyncPurchaseCreate(purchase);
		
		return purchase ; 
	}

	@Transactional
	public Purchase updatePurchase(PurchaseUpdateRequest purchaseUpdateRequest) {
		
		Purchase purchase = purchaseRepository.findPurchase(purchaseUpdateRequest.getPurchaseCode());
		
		Preconditions.checkArgument(purchase != null , "采购单不存在");
		
		if(StrUtil.isNotBlank(purchaseUpdateRequest.getComments())) {
			purchase.setComments(purchaseUpdateRequest.getComments().trim());
		}
		if(StrUtil.isNotBlank(purchaseUpdateRequest.getPurchaseUrl())) {
			purchase.setPurchaseUrl(purchaseUpdateRequest.getPurchaseUrl().trim());
		}
		if(StrUtil.isNotBlank(purchaseUpdateRequest.getSupplierName())) {
			purchase.setSupplierName(purchaseUpdateRequest.getSupplierName().trim());
		}
		if(purchaseUpdateRequest.getPurchasePrice() != null) {
			purchase.setPurchasePrice(purchaseUpdateRequest.getPurchasePrice());
		}
		if(purchaseUpdateRequest.getPurchaseFreightPrice() != null) {
			purchase.setPurchaseFreightPrice(purchaseUpdateRequest.getPurchaseFreightPrice());
		}
		
		return purchaseRepository.save(purchase);
	} 
	
	@Transactional
	public void deletePurchase(String purchaseCode) {
		
		Purchase purchase = purchaseRepository.findPurchase(purchaseCode);
		
		Preconditions.checkArgument(purchase != null , "采购单不存在");
		
		purchase.setStatus(PurchaseStatus.PURCHASE_CANCEL.getStatus());
		
		purchaseRepository.save(purchase);
		
		List<PurchaseLogistics> purchaseLogistics = purchaseLogisticsRepository.findPurchaseLogistics(purchaseCode);
		purchaseLogisticsRepository.deleteAll(purchaseLogistics);
		
		purchaseSchedule.asyncPurchaseDelete(purchaseCode);
		
	}
	

	public Page<PurchaseSearchDTO> findPurchase(PurchaseSearchRequest purchaseSearchRequest, Pageable pageable) {
		
		User currentUser = SecurityUtils.getCurrentUser();
		
		String contentSQL =   "SELECT DISTINCT \n"
							+ "	p_purchase.id purchase_id, p_purchase.purchase_code, p_purchase.purchase_url, p_purchase.storehouse_id, p_purchase.supplier_name, p_purchase.purchase_price, p_purchase.purchase_freight_price, p_purchase.comments, p_purchase.purchased_total_quantity, p_purchase.storaged_total_quantity, p_purchase.cancel_total_quantity, p_purchase.`status`, p_purchase.created_at, \n"
							+"	s_storehouse.storehouse_name \n"
							+ "FROM p_purchase \n" 
							+"	LEFT JOIN s_storehouse ON (p_purchase.storehouse_id = s_storehouse.id) \n";
		
		StringBuilder leftjoinSQL = new StringBuilder();
		
		StringBuilder paramsSQL = new StringBuilder("WHERE p_purchase.yn = 1 \n");
		Map<String , Object> paramsMap = new HashMap<>();
		
		if(StrUtil.isNotBlank(purchaseSearchRequest.getPurchaseCode())) {
			if(StrUtil.contains(purchaseSearchRequest.getPurchaseCode(), ',')) {
				paramsSQL.append("AND TRIM(p_purchase.purchase_code) IN (:purchaseCodes) \n");
				paramsMap.put("purchaseCodes", StrUtil.split(purchaseSearchRequest.getPurchaseCode().trim(), ','));
			}else {
				paramsSQL.append("AND TRIM(p_purchase.purchase_code) = :purchaseCode \n");
				paramsMap.put("purchaseCode", purchaseSearchRequest.getPurchaseCode().trim());
			}
		}
		if(purchaseSearchRequest.getStorehouseId() != null) {
			paramsSQL.append("AND p_purchase.storehouse_id = :storehouseId \n");
			paramsMap.put("storehouseId", purchaseSearchRequest.getStorehouseId());
		}
		if(purchaseSearchRequest.getStatus() != null) {
			paramsSQL.append("AND p_purchase.`status` = :status \n");
			paramsMap.put("status", purchaseSearchRequest.getStatus());
		}
		if(StrUtil.isNotBlank(purchaseSearchRequest.getSupplierName())) {
			paramsSQL.append("AND p_purchase.supplier_name LIKE :supplierName \n");
			paramsMap.put("supplierName", StrUtil.wrap(purchaseSearchRequest.getSupplierName().trim(), "%"));
		}
		if(StrUtil.isNotBlank(purchaseSearchRequest.getComments())) {
			paramsSQL.append("AND p_purchase.comments = :comments \n");
			paramsMap.put("comments", purchaseSearchRequest.getComments().trim());
		}
		List<String> roles = currentUser.getRoles();
		if(roles.size() == 1 && roles.contains("storehouse")) {
			paramsSQL.append("AND s_storehouse.managers like :userName \n");
			paramsMap.put("userName", StrUtil.wrap(currentUser.getUsername(), "%"));
		}
		
		if(StrUtil.isNotBlank(purchaseSearchRequest.getLogisticCode())) {
			leftjoinSQL.append("	LEFT JOIN p_purchase_logistics ON (p_purchase.purchase_code = p_purchase_logistics.purchase_code AND p_purchase_logistics.yn = 1) \n");
			if(StrUtil.contains(purchaseSearchRequest.getLogisticCode(), ',')) {
				paramsSQL.append("AND p_purchase_logistics.logistic_code IN (:logisticCodes) \n");
				paramsMap.put("logisticCodes", StrUtil.split(purchaseSearchRequest.getLogisticCode().trim(), ','));
			}else {
				paramsSQL.append("AND p_purchase_logistics.logistic_code = :logisticCode \n");
				paramsMap.put("logisticCode", purchaseSearchRequest.getLogisticCode().trim());
			}
		}
		if(purchaseSearchRequest.getProductId() != null || StrUtil.isNotBlank(purchaseSearchRequest.getProductSkuBarcode())) {
			leftjoinSQL.append("	LEFT JOIN p_purchase_product ON (p_purchase.purchase_code = p_purchase_product.purchase_code AND p_purchase_product.yn = 1) \n");
			if(purchaseSearchRequest.getProductId() != null) {
				paramsSQL.append("AND p_purchase_product.product_id = :productId \n");
				paramsMap.put("productId", purchaseSearchRequest.getProductId());
			}
			if(StrUtil.isNotBlank(purchaseSearchRequest.getProductSkuBarcode())) {
				paramsSQL.append("AND p_purchase_product.product_sku_barcode = :productSkuBarcode \n");
				paramsMap.put("productSkuBarcode", purchaseSearchRequest.getProductSkuBarcode().trim());
			}
		}
		if(purchaseSearchRequest.getBegainTime() != null && purchaseSearchRequest.getEndTime() != null) {
			paramsSQL.append("AND p_purchase.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
			paramsMap.put("begainTime", purchaseSearchRequest.getBegainTime());
			paramsMap.put("endTime", purchaseSearchRequest.getEndTime());
		}
		
		String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
		String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
		
		List<PurchaseSearchDTO> content = jdbcTemplate.query(contentSQL + leftjoinSQL + paramsSQL + orderSQL + pageSQL, paramsMap,  new BeanPropertyRowMapper<>(PurchaseSearchDTO.class));
		
		for (PurchaseSearchDTO purchaseSearchDTO : content) {
			PurchaseStatus purchaseStatus = PurchaseStatus.OrderStatusByStatus(purchaseSearchDTO.getStatus());
			purchaseSearchDTO.setStatusName(purchaseStatus == null ? "未知" : purchaseStatus.getStatusName());
			purchaseSearchDTO.setPurchaseLogisticsSearchDTOs(findPurchaseLogistics(purchaseSearchDTO.getPurchaseCode()));
			purchaseSearchDTO.setPurchaseProductSearchDTOs(findPurchaseProduct(purchaseSearchDTO.getPurchaseCode()));
		}
		
		
		Long total = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM (" + contentSQL + leftjoinSQL + paramsSQL + ") T", paramsMap, Long.class);
		
		return new PageImpl<>(content, pageable, total);
	}
	
	public Page<PurchaseSimpleExportDTO> findSimplePurchase(PurchaseSearchRequest purchaseSearchRequest, Pageable pageable) {
		
		User currentUser = SecurityUtils.getCurrentUser();
		
		String contentSQL =   "SELECT DISTINCT \n"
							+ "	p_purchase.purchase_code, p_purchase.purchase_url, p_purchase.supplier_name, p_purchase.purchase_price, p_purchase.purchase_freight_price, p_purchase.comments, p_purchase.`status`, p_purchase.created_at, \n"
							+"	s_storehouse.storehouse_name \n"
							+ "FROM p_purchase \n" 
							+"	LEFT JOIN s_storehouse ON (p_purchase.storehouse_id = s_storehouse.id) \n";
		
		StringBuilder leftjoinSQL = new StringBuilder();
		
		StringBuilder paramsSQL = new StringBuilder("WHERE p_purchase.yn = 1 \n");
		Map<String , Object> paramsMap = new HashMap<>();
		
		if(StrUtil.isNotBlank(purchaseSearchRequest.getPurchaseCode())) {
			if(StrUtil.contains(purchaseSearchRequest.getPurchaseCode(), ',')) {
				paramsSQL.append("AND TRIM(p_purchase.purchase_code) IN (:purchaseCodes) \n");
				paramsMap.put("purchaseCodes", StrUtil.split(purchaseSearchRequest.getPurchaseCode().trim(), ','));
			}else {
				paramsSQL.append("AND TRIM(p_purchase.purchase_code) = :purchaseCode \n");
				paramsMap.put("purchaseCode", purchaseSearchRequest.getPurchaseCode().trim());
			}
		}
		if(purchaseSearchRequest.getStorehouseId() != null) {
			paramsSQL.append("AND p_purchase.storehouse_id = :storehouseId \n");
			paramsMap.put("storehouseId", purchaseSearchRequest.getStorehouseId());
		}
		if(purchaseSearchRequest.getStatus() != null) {
			paramsSQL.append("AND p_purchase.`status` = :status \n");
			paramsMap.put("status", purchaseSearchRequest.getStatus());
		}
		if(StrUtil.isNotBlank(purchaseSearchRequest.getSupplierName())) {
			paramsSQL.append("AND p_purchase.supplier_name LIKE :supplierName \n");
			paramsMap.put("supplierName", StrUtil.wrap(purchaseSearchRequest.getSupplierName().trim(), "%"));
		}
		if(StrUtil.isNotBlank(purchaseSearchRequest.getComments())) {
			paramsSQL.append("AND p_purchase.comments = :comments \n");
			paramsMap.put("comments", purchaseSearchRequest.getComments().trim());
		}
		List<String> roles = currentUser.getRoles();
		if(roles.size() == 1 && roles.contains("storehouse")) {
			paramsSQL.append("AND s_storehouse.managers like :userName \n");
			paramsMap.put("userName", StrUtil.wrap(currentUser.getUsername(), "%"));
		}
		if(StrUtil.isNotBlank(purchaseSearchRequest.getLogisticCode())) {
			leftjoinSQL.append("	LEFT JOIN p_purchase_logistics ON (p_purchase.purchase_code = p_purchase_logistics.purchase_code AND p_purchase_logistics.yn = 1) \n");
			if(StrUtil.contains(purchaseSearchRequest.getLogisticCode(), ',')) {
				paramsSQL.append("AND p_purchase_logistics.logistic_code IN (:logisticCodes) \n");
				paramsMap.put("logisticCodes", StrUtil.split(purchaseSearchRequest.getLogisticCode().trim(), ','));
			}else {
				paramsSQL.append("AND p_purchase_logistics.logistic_code = :logisticCode \n");
				paramsMap.put("logisticCode", purchaseSearchRequest.getLogisticCode().trim());
			}
		}
		if(purchaseSearchRequest.getProductId() != null || StrUtil.isNotBlank(purchaseSearchRequest.getProductSkuBarcode())) {
			leftjoinSQL.append("	LEFT JOIN p_purchase_product ON (p_purchase.purchase_code = p_purchase_product.purchase_code AND p_purchase_product.yn = 1) \n");
			if(purchaseSearchRequest.getProductId() != null) {
				paramsSQL.append("AND p_purchase_product.product_id = :productId \n");
				paramsMap.put("productId", purchaseSearchRequest.getProductId());
			}
			if(StrUtil.isNotBlank(purchaseSearchRequest.getProductSkuBarcode())) {
				paramsSQL.append("AND p_purchase_product.product_sku_barcode = :productSkuBarcode \n");
				paramsMap.put("productSkuBarcode", purchaseSearchRequest.getProductSkuBarcode().trim());
			}
		}
		if(purchaseSearchRequest.getBegainTime() != null && purchaseSearchRequest.getEndTime() != null) {
			paramsSQL.append("AND p_purchase.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
			paramsMap.put("begainTime", purchaseSearchRequest.getBegainTime());
			paramsMap.put("endTime", purchaseSearchRequest.getEndTime());
		}
		
		String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
		String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
		
		List<PurchaseSimpleExportDTO> content = jdbcTemplate.query(contentSQL + leftjoinSQL + paramsSQL + orderSQL + pageSQL, paramsMap,  new BeanPropertyRowMapper<>(PurchaseSimpleExportDTO.class));
		
		for (PurchaseSimpleExportDTO purchaseSearchDTO : content) {
			PurchaseStatus purchaseStatus = PurchaseStatus.OrderStatusByStatus(purchaseSearchDTO.getStatus());
			purchaseSearchDTO.setStatusName(purchaseStatus == null ? "未知" : purchaseStatus.getStatusName());
		}
		
		
		Long total = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM (" + contentSQL + leftjoinSQL + paramsSQL + ") T", paramsMap, Long.class);
		
		return new PageImpl<>(content, pageable, total);
	}

	public List<PurchaseProductSearchDTO> findPurchaseProduct(String purchaseCode) {
		
		List<PurchaseProductSearchDTO> result = new ArrayList<>();
		
		List<PurchaseProduct> purchaseProducts = purchaseProductRepository.findPurchaseProduct(purchaseCode);
		
		for (PurchaseProduct purchaseProduct : purchaseProducts) {
			PurchaseProductSearchDTO dto = new PurchaseProductSearchDTO();
			BeanUtils.copyProperties(purchaseProduct, dto);
			
			result.add(dto);
		}
		
		return result ; 
	}

	
	public List<PurchaseLogisticsSearchDTO> findPurchaseLogistics(String purchaseCode){
		
		List<PurchaseLogisticsSearchDTO> result = new ArrayList<>();
		
		List<PurchaseLogistics> purchaseLogistics = purchaseLogisticsRepository.findPurchaseLogistics(purchaseCode);
		
		for (PurchaseLogistics purchaseLogistic : purchaseLogistics) {
			
			PurchaseLogisticsSearchDTO purchaseLogisticsSearchDTO = new PurchaseLogisticsSearchDTO();
			BeanUtils.copyProperties(purchaseLogistic, purchaseLogisticsSearchDTO);
			purchaseLogisticsSearchDTO.setLogisticId(purchaseLogistic.getId());
			result.add(purchaseLogisticsSearchDTO);
		}
		return result ; 
	}
	

	@Transactional
	public PurchaseProduct updatePurchaseProduct(PurchaseProductUpdateRequest purchaseProductUpdateRequest) {
		
		Preconditions.checkArgument(purchaseProductUpdateRequest.getPurchaseProductId() != null, "未找到要修改的数据标识");
		
		PurchaseProduct source = purchaseProductRepository.findPurchaseProduct(purchaseProductUpdateRequest.getPurchaseProductId());
		
		Preconditions.checkArgument(source != null, "要修改的数据不存在");
		
		Purchase purchase = purchaseRepository.findPurchase(purchaseProductUpdateRequest.getPurchaseCode());
		
		Preconditions.checkArgument(purchase != null, "对应的采购单数据不存在");
		
		if(purchaseProductUpdateRequest.getPurchasedQuantity() != null) {
			
			purchase.setPurchasedTotalQuantity(purchase.getPurchasedTotalQuantity() + Math.abs(purchaseProductUpdateRequest.getPurchasedQuantity()) - Math.abs(source.getPurchasedQuantity()));
			
			source.setPurchasedQuantity(Math.abs(purchaseProductUpdateRequest.getPurchasedQuantity()));
			
		}
		if(purchaseProductUpdateRequest.getStoragedQuantity() != null) {
			
			purchase.setStoragedTotalQuantity(purchase.getStoragedTotalQuantity() + Math.abs(purchaseProductUpdateRequest.getStoragedQuantity()) - Math.abs(source.getStoragedQuantity()));
			
			source.setStoragedQuantity(Math.abs(purchaseProductUpdateRequest.getStoragedQuantity()));
		}
		if(purchaseProductUpdateRequest.getCancelQuantity() != null) {
			
			purchase.setCancelTotalQuantity(purchase.getCancelTotalQuantity() + Math.abs(purchaseProductUpdateRequest.getCancelQuantity()) - Math.abs(source.getCancelQuantity()));
			
			source.setCancelQuantity(Math.abs(purchaseProductUpdateRequest.getCancelQuantity()));
		}
		if(purchaseProductUpdateRequest.getPurchasedPrice() != null) {
			source.setPurchasedPrice(purchaseProductUpdateRequest.getPurchasedPrice());
		}
		
		if((purchase.getPurchasedTotalQuantity() - purchase.getStoragedTotalQuantity() - purchase.getCancelTotalQuantity()) <= 0) {
			purchase.setStatus(PurchaseStatus.PURCHASE_OK.getStatus());
		}
		
		purchaseRepository.save(purchase);
		
		purchaseProductRepository.save(source);
		return source;
	}


	@Transactional
	public PurchaseProduct addPurchaseProduct(PurchaseProductUpdateRequest purchaseProductUpdateRequest) {
		
		Purchase purchase = purchaseRepository.findPurchase(purchaseProductUpdateRequest.getPurchaseCode());
		
		Preconditions.checkArgument(purchase != null, "对应的采购单数据不存在");
		
		Preconditions.checkArgument(purchase.getStatus() == PurchaseStatus.PURCHASE_CREATE.getStatus() || purchase.getStatus() == PurchaseStatus.PURCHASE_ASYNC.getStatus() , "只能是新建或者收货过程中的采购单允许该操作");
		
		Preconditions.checkArgument(purchaseProductUpdateRequest.getProductId() != null, "采购单商品信息不完整");
		Preconditions.checkArgument(purchaseProductUpdateRequest.getPurchasedPrice() != null, "采购单商品信息不完整");
		Preconditions.checkArgument(purchaseProductUpdateRequest.getProductSkuId() != null, "采购单商品信息不完整");
		Preconditions.checkArgument(purchaseProductUpdateRequest.getPurchasedQuantity() != null, "采购单商品信息不完整");
		Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductUpdateRequest.getProductTitle()), "采购单商品信息不完整");
		Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductUpdateRequest.getProductSkuBarcode()), "采购单商品信息不完整");
		Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductUpdateRequest.getProductSkuName()), "采购单商品信息不完整");
		Preconditions.checkArgument(StrUtil.isNotBlank(purchaseProductUpdateRequest.getProductSkuImage()), "采购单商品信息不完整");
		
		PurchaseProduct purchaseProduct = new PurchaseProduct();
		BeanUtils.copyProperties(purchaseProductUpdateRequest, purchaseProduct);
		
		purchaseProductRepository.save(purchaseProduct);
		
		return purchaseProduct;
	}

	@Transactional
	public void deleteLogistics(Long logisticsId) {
		
		PurchaseLogistics purchaseLogistics = purchaseLogisticsRepository.findById(logisticsId).orElse(null);
		
		Preconditions.checkArgument(purchaseLogistics != null, "信息不存在...");
		
		purchaseLogisticsRepository.delete(purchaseLogistics);
	}


	@Transactional
	public void addPurchaseLogistics(PurchaseLogisticsRequest purchaseLogisticsRequest) {
		
		PurchaseLogistics logistics = new PurchaseLogistics();
		BeanUtils.copyProperties(purchaseLogisticsRequest, logistics);
		
		purchaseLogisticsRepository.save(logistics);
		
		purchaseSchedule.asyncPurchaseLogisticsAdd(logistics.getPurchaseCode(), logistics.getLogisticCode(), logistics.getLogisticCompany());
	}

	@Transactional
	public void updatePurchaseLogistics(PurchaseLogisticsRequest purchaseLogisticsRequest) {
		
		PurchaseLogistics purchaseLogistics = purchaseLogisticsRepository.findById(purchaseLogisticsRequest.getLogisticId()).get();
		
		Preconditions.checkArgument(purchaseLogistics != null, "物流不存在");
		
		PurchaseLogistics findPurchaseLogistics = purchaseLogisticsRepository.findPurchaseLogisticsWithLogisticCode(purchaseLogisticsRequest.getNewLogisticCode());
		Preconditions.checkArgument(findPurchaseLogistics == null || findPurchaseLogistics.getId().equals(purchaseLogistics.getId()) , "物流单号已存在");
		
		if(StrUtil.isNotBlank(purchaseLogisticsRequest.getCarrierCode()))purchaseLogistics.setCarrierCode(purchaseLogisticsRequest.getCarrierCode());
		if(StrUtil.isNotBlank(purchaseLogisticsRequest.getLogisticCompany()))purchaseLogistics.setLogisticCompany(purchaseLogisticsRequest.getLogisticCompany());
		if(StrUtil.isNotBlank(purchaseLogisticsRequest.getNewLogisticCode()))purchaseLogistics.setLogisticCode(purchaseLogisticsRequest.getNewLogisticCode());
		
		purchaseLogisticsRepository.save(purchaseLogistics);
		
		purchaseSchedule.asyncPurchaseLogisticsAdd(purchaseLogistics.getPurchaseCode(), purchaseLogistics.getLogisticCode(), purchaseLogistics.getLogisticCompany());
	}


	/**
	 *  收货
	 * @param storageProductRequest
	 */
	@Transactional
	public void StorageProduct(PurchaseStorageProductRequest storageProductRequest) {
		
		Purchase purchase = purchaseRepository.findPurchase(storageProductRequest.getBatchNumber());
		Preconditions.checkArgument(purchase != null, "采购单不存在...");
		
		PurchaseProduct purchaseProduct = purchaseProductRepository.findPurchaseProduct(storageProductRequest.getBatchNumber() , storageProductRequest.getProductSkuBarcode());
		Preconditions.checkArgument(purchase != null, "采购单SKU不存在...");
		
		purchaseProduct.setStoragedQuantity(purchaseProduct.getStoragedQuantity() + Math.abs(storageProductRequest.getQuantity()));
		
		purchase.setStoragedTotalQuantity(purchase.getStoragedTotalQuantity() + Math.abs(storageProductRequest.getQuantity()));
		purchaseRepository.save(purchase);

		Inventory inventory = inventoryRepository.findInventory(purchase.getStorehouseId(), storageProductRequest.getProductSkuBarcode());
		if(inventory == null) {
			inventory = new Inventory(purchaseProduct.getProductTitle(), purchaseProduct.getProductSkuBarcode(), purchaseProduct.getProductSkuName(), purchaseProduct.getProductSkuImage(), 0, purchase.getStorehouseId(), "采购单"+storageProductRequest.getBatchNumber()+"收货新建");
		}
		inventory.setInventory(inventory.getInventory() + Math.abs(storageProductRequest.getQuantity()));
		inventoryRepository.save(inventory);
		
		//说明一个SKU完成
		if(purchaseProduct.getPurchasedQuantity() <= purchaseProduct.getStoragedQuantity() + purchaseProduct.getCancelQuantity()) {
			
			List<PurchaseProduct> purchaseProducts = purchaseProductRepository.findPurchaseProduct(storageProductRequest.getBatchNumber());
			
			boolean isAllOk = true ; 
			for (PurchaseProduct _purchaseProduct : purchaseProducts) {
				if(!StrUtil.equalsIgnoreCase(_purchaseProduct.getProductSkuBarcode(), storageProductRequest.getProductSkuBarcode())) {
					if(_purchaseProduct.getPurchasedQuantity() - _purchaseProduct.getCancelQuantity() - _purchaseProduct.getStoragedQuantity() > 0) {
						isAllOk = false ; 
						break;
					}
				}
			}
			if(isAllOk) {
				purchase.setStatus(PurchaseStatus.PURCHASE_OK.getStatus());
				purchaseRepository.save(purchase);
			}
		}
	}
}
